package com.evil.activiti.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.evil.activiti.delegate.RobotDelegate;
import com.evil.activiti.enums.EventTypeEnum;
import com.evil.activiti.listener.*;
import com.evil.common.activiti.enums.*;
import com.evil.common.application.dto.judge.JudgeOr;
import com.evil.common.application.dto.process.ProcessDeploymentReqDTO;
import com.evil.common.application.dto.process.node.NodeBaseDTO;
import com.evil.common.application.dto.process.node.NodeRespInfoDTO;
import com.evil.common.application.dto.process.node.gateway.GatewayConfigDTO;
import com.evil.common.application.dto.robot.RobotConfigDTO;
import com.evil.common.core.enums.BaseEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.function.Callback;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.family.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import lombok.Data;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.delegate.BaseExecutionListener;
import org.activiti.engine.delegate.BaseTaskListener;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流工具栏
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Data
public class BpmnModelUtil {

    private BpmnModel bpmnModel;

    private Process process;

    /**
     * 当前网关节点Y轴高度
     */
    private Map<String, Double> currentParallelNodeHeightMap = new LinkedHashMap<>();

    /**
     * 节点记录
     */
    private Map<Long, Task> nodeTaskMap = new LinkedHashMap<>();

    /**
     * 创建执行监听器
     *
     * @param eventTypeEnum eventTypeEnum
     * @param listener      listener
     * @return ActivitiListener
     */
    public static ActivitiListener createExecutionListener(EventTypeEnum eventTypeEnum, Class<? extends BaseExecutionListener> listener) {
        ActivitiListener activitiListener = new ActivitiListener();
        activitiListener.setEvent(eventTypeEnum.getEvent());
        activitiListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        activitiListener.setImplementation(String.format("${%s}", listener.getSimpleName()));
        return activitiListener;
    }

    /**
     * 创建会话监听器
     *
     * @param eventTypeEnum eventTypeEnum
     * @param listener      listener
     * @return ActivitiListener
     */
    public static ActivitiListener createTaskListener(EventTypeEnum eventTypeEnum, Class<? extends BaseTaskListener> listener) {
        ActivitiListener activitiListener = new ActivitiListener();
        activitiListener.setEvent(eventTypeEnum.getEvent());
        activitiListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        activitiListener.setImplementation(String.format("${%s}", listener.getSimpleName()));
        return activitiListener;
    }

    /**
     * 获取指定节点可回退节点
     *
     * @param flowElement flowElement
     * @return List
     */
    public static List<FlowNode> getCanFallbacks(FlowElement flowElement) {
        return BpmnModelUtil.getFamilyFlows(flowElement, true).stream()
                // 过滤可回退节点
                .filter(e -> e instanceof UserTask && NodeTypeEnum.isExistById(ActivityUtil.restoreFlowNodeType(e.getId())).canFallback())
                .map(e -> (FlowNode) e)
                .collect(Collectors.toList());
    }

    /**
     * 获取指定节点所有家族（只获取流转线连接的家族 SequenceFlowTypeEnum.COMPLETE）
     *
     * @param flowElement flowElement
     * @param isUp        递归找父亲/递归找儿子
     * @return List
     */
    public static List<FlowElement> getFamilyFlows(FlowElement flowElement, boolean isUp) {
        List<FlowElement> flowElements = new ArrayList<>();
        FlowNode flowNode = null;
        // 一下先写成 强转成具体的实体 而不是 FlowNode，以便以后拓展
        if (flowElement instanceof StartEvent) {
            flowNode = (StartEvent) flowElement;
        } else if (flowElement instanceof EndEvent) {
            flowNode = (EndEvent) flowElement;
        } else if (flowElement instanceof InclusiveGateway) {
            flowNode = (InclusiveGateway) flowElement;
        } else if (flowElement instanceof ExclusiveGateway) {
            flowNode = (ExclusiveGateway) flowElement;
        } else if (flowElement instanceof UserTask) {
            flowNode = (UserTask) flowElement;
        } else if (flowElement instanceof ServiceTask) {
            flowNode = (ServiceTask) flowElement;
        }
        List<SequenceFlow> sequenceFlows = new ArrayList<>();
        if (null != flowNode) {
            sequenceFlows.addAll(isUp ? flowNode.getIncomingFlows() : flowNode.getOutgoingFlows());
        }
        // 严格控制 递归添加时 顺序
        sequenceFlows.stream()
                // 只获取 流转线连接的 家族节点
                .filter(e -> SequenceFlowTypeEnum.COMPLETE.getType().equals(ActivityUtil.restoreFlowType(e.getId())))
                .forEach(e -> {
                    // 找父亲则获取来源，找儿子则获取目标
                    FlowElement currentFlowElement = isUp ? e.getSourceFlowElement() : e.getTargetFlowElement();

                    // 不添加重复节点
                    Map<String, FlowElement> map = StreamUtil.toMapK(flowElements, BaseElement::getId);
                    if (!map.containsKey(currentFlowElement.getId())) {
                        flowElements.add(currentFlowElement);
                    }
                    BpmnModelUtil.getFamilyFlows(currentFlowElement, isUp).forEach(f -> {
                        if (!map.containsKey(f.getId())) {
                            flowElements.add(f);
                        }
                    });
                });

        return flowElements;
    }

    /**
     * 创建临时流转线
     *
     * @param source source
     * @param target target
     */
    public static void tempSequenceFlow(FlowNode source, FlowNode target, SequenceFlowTypeEnum sequenceFlowTypeEnum, ActivitiListener activitiListener, Callback callback) {
        // 记录原活动方向
        List<SequenceFlow> souSequenceFlows = new ArrayList<>(source.getOutgoingFlows());

        // 清理活动方向
        source.getOutgoingFlows().clear();

        // 建立临时新方向
        SequenceFlow sequenceFlow = BpmnModelUtil.createSequenceFlow(sequenceFlowTypeEnum, source, target, null, new ArrayList<>(), false);
        source.setOutgoingFlows(CollectionUtil.newArrayList(sequenceFlow));
        if (null != activitiListener) {
            sequenceFlow.setExecutionListeners(Collections.singletonList(activitiListener));
        }

        // 回调
        try {
            callback.apply();
        } catch (Exception e) {
            throw new BusinessException(RCodeEnum.ACTIVITY_COMPLETE_ERROR);
        } finally {
            // 恢复原方向
            source.setOutgoingFlows(souSequenceFlows);
        }
    }

    public static SequenceFlow createSequenceFlow(SequenceFlowTypeEnum flowTypeEnum, FlowNode source, FlowNode target, String flowId, List<JudgeOr> judgeOrs, boolean negation) {
        SequenceFlow sequenceFlow = new SequenceFlow(source.getId(), target.getId());
        if (null == flowId) {
            flowId = ActivityUtil.connector(flowTypeEnum.getType(), source.getId(), target.getId());
        }
        sequenceFlow.setId(flowId);
        sequenceFlow.setName(flowId);
        sequenceFlow.setSourceRef(source.getId());
        sequenceFlow.setSourceFlowElement(source);
        sequenceFlow.setTargetRef(target.getId());
        sequenceFlow.setTargetFlowElement(target);
        // 跳过表达式
//        sequenceFlow.setSkipExpression("");
        sequenceFlow.setConditionExpression(ActivityUtil.conditionExpression(judgeOrs, negation));
        List<ActivitiListener> executionListeners = sequenceFlow.getExecutionListeners();
        executionListeners.add(BpmnModelUtil.createExecutionListener(EventTypeEnum.TAKE, FlowTakeListener.class));
        sequenceFlow.setExecutionListeners(executionListeners);
        return sequenceFlow;
    }

//    /**
//     * 创建表单
//     *
//     * @return FormProperty
//     */
//    private FormProperty createFormProperty() {
//        FormProperty formProperty = new FormProperty();
//        formProperty.setName("");
//        formProperty.setExpression("");
//        formProperty.setVariable("");
//        formProperty.setType("");
//        formProperty.setDefaultExpression("");
//        formProperty.setDatePattern("");
//        formProperty.setReadable(true);
//        formProperty.setWriteable(true);
//        formProperty.setRequired(true);
//        return formProperty;
//    }

    /**
     * 创建流程model
     *
     * @param processDeploymentReqDTO processDeploymentReqDTO
     * @return BpmnModel
     */
    public BpmnModel createBpmnModel(ProcessDeploymentReqDTO processDeploymentReqDTO) {
        // 实例化BpmnModel对象
        BpmnModel bpmnModel = new BpmnModel();
        this.bpmnModel = bpmnModel;
        bpmnModel.addProcess(this.createProcess(processDeploymentReqDTO));
        return bpmnModel;
    }

//    /**
//     * 创建定时器
//     *
//     * @param nodeRespDTO nodeRespDTO
//     * @return BpmnModel
//     */
//    private TimerEventDefinition createTimerEventDefinition(NodeRespInfoDTO nodeRespDTO) {
//        TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
//        timerEventDefinition.setId(ActivityUtil.generateEventDefinitionId(nodeRespDTO));
//        timerEventDefinition.setCalendarName("");
//        timerEventDefinition.setEndDate("");
//        timerEventDefinition.setTimeCycle("");
//        timerEventDefinition.setTimeDate("");
//        timerEventDefinition.setTimeDuration("");
//        return timerEventDefinition;
//    }

    /**
     * 创建流程Process
     *
     * @param processDeploymentReqDTO processDeploymentReqDTO
     * @return BpmnModel
     */
    private Process createProcess(ProcessDeploymentReqDTO processDeploymentReqDTO) {
        // Process对象
        Process process = new Process();

        process.setId(ActivityUtil.getProcessId(processDeploymentReqDTO.getProcessId()));
        this.process = process;

        // 递归初始化节点
        this.init(null, processDeploymentReqDTO.getNode(), false);

        // 添加一个特殊停止节点，用于操作 回退节点时，终止其他并行任务
        this.addStopNode();
        // 添加一个审批拒绝节点
        this.addRefuseNode();

        return process;
    }

//    /**
//     * 生成回退配置
//     *
//     * @param flowNode    flowNode
//     * @param nodeRespDTO nodeRespDTO
//     * @return ExclusiveGateway
//     */
//    private ExclusiveGateway initFallback(FlowNode flowNode, NodeRespInfoDTO nodeRespDTO) {
//        if (NodeTypeEnum.APPROVAL_NODE.getId() == nodeRespDTO.getNodeType()) {
//            // 流程回退 节点负责人可将流程回退到之前的节点，仅审批节点可用
//            if (SwitchEnum.YES.getId() == nodeRespDTO.getNodeFallback()) {
//                // 开启回退配置
//                if (null != nodeRespDTO.getNodeFallbackConfig()) {
//                    FallbackConfigDTO nodeFallbackConfig = nodeRespDTO.getNodeFallbackConfig();
//                    // 回退列表
//                    List<FlowNode> fallbackFlowNodes = new ArrayList<>();
//                    if (FallbackTypeEnum.ALL_BEFORE.getId() == nodeFallbackConfig.getFallbackType()) {
//                        // 之前所有节点 获取当前节点所有上级节点
//                        fallbackFlowNodes = BpmnModelUtil.getCanFallbacks(flowNode);
//                    } else if (FallbackTypeEnum.ONE_BEFORE.getId() == nodeFallbackConfig.getFallbackType()) {
//                        // 上一节点 获取当前节点所有上级节点
//                        BpmnModelUtil.getCanFallbacks(flowNode).stream().findFirst().ifPresent(fallbackFlowNodes::add);
//                    } else if (FallbackTypeEnum.ASSIGN_BEFORE.getId() == nodeFallbackConfig.getFallbackType()) {
//                        // 指定节点
//                        fallbackFlowNodes.addAll(nodeFallbackConfig.getFallbackNodeIds().stream().map(nodeId -> {
//                            if (!nodeTaskMap.containsKey(nodeId)) {
//                                throw new BusinessException(BaseEnum.RUN_ERROR);
//                            }
//                            return nodeTaskMap.get(nodeId);
//                        }).toList());
//                    }
//
//                    // 获取当前节点 的流转排他网关节点
//                    ExclusiveGateway exclusiveGateway = (ExclusiveGateway) this.getGatewayByFlowNode(flowNode, nodeRespDTO, NodeTypeEnum.EXCLUSIVE_GATEWAY_NODE);
//
//                    // 生成回退 从排他网关回退指定节点
//                    fallbackFlowNodes.forEach(fallbackFlow -> {
//                        List<JudgeOr> ors = ActivityUtil.getProcessNodeCondition(ActivityUtil.getProcessNodeFallBackCondition(fallbackFlow.getId()));
//                        this.initSequenceFlow(SequenceFlowTypeEnum.FALLBACK, exclusiveGateway, fallbackFlow, null, ors, false);
//                    });
//                    return exclusiveGateway;
//                }
//            }
//        }
//        return null;
//    }

//    /**
//     * 生成流程拒绝配置
//     *
//     * @param flowNode    flowNode
//     * @param nodeRespDTO nodeRespDTO
//     * @return ExclusiveGateway
//     */
//    private ExclusiveGateway initRefuse(FlowNode flowNode, NodeRespInfoDTO nodeRespDTO) {
//        if (NodeTypeEnum.APPROVAL_NODE.getId() == nodeRespDTO.getNodeType()) {
//            // 流程拒绝 节点负责人拒绝流程终止流程
//            if (SwitchEnum.YES.getId() == nodeRespDTO.getNodeRefuse()) {
//                // 获取当前节点 的流转排他网关节点
//                ExclusiveGateway exclusiveGateway = (ExclusiveGateway) this.getGatewayByFlowNode(flowNode, nodeRespDTO, NodeTypeEnum.EXCLUSIVE_GATEWAY_NODE);
//
//                // 当前节点取负数，作为拒绝结束节点
//                NodeRespInfoDTO refuseNode = new NodeRespInfoDTO();
//                refuseNode.setNodeId(-nodeRespDTO.getNodeId());
//                refuseNode.setNodeType(NodeTypeEnum.END_EVENT.getId());
//                refuseNode.setNodeName(HandleTypeEnum.REJECTED.getName());
//
//                // 连接线
//                List<JudgeOr> ors = ActivityUtil.getProcessNodeCondition(ActivityUtil.getProcessNodeRefuseCondition());
//                this.appendFlowNode(exclusiveGateway, refuseNode, SequenceFlowTypeEnum.REFUSE, ors);
//
//                return exclusiveGateway;
//            }
//        }
//        return null;
//    }

    private FlowNode init(FlowNode preNode, NodeRespInfoDTO node, boolean returnEndNode) {
        FlowNode flowNode = this.createElement(preNode, node, null, true);

        if (null != preNode) {
            // 连线条件
            List<JudgeOr> judgeOrs = new ArrayList<>();
            boolean negation = false;
            if (preNode instanceof InclusiveGateway) {
                if (NodeTypeEnum.BRANCH_INCLUSIVE_GATEWAY_NODE.getId() != ActivityUtil.restoreFlowNodeType(preNode.getId())) {
                    // 网关条件
                    judgeOrs = node.getGatewayConfig().getJudgeOr();
                    negation = node.getGatewayConfig().getNegation();
                }
            } else {
                // 如果上一节点为开始节点/抄送节点/机器人，则不需要流转条件
                if (!(preNode instanceof StartEvent
                        || preNode instanceof ServiceTask
                        || NodeTypeEnum.CARBON_COPY_NODE.getId() == ActivityUtil.restoreFlowNodeType(preNode.getId()))) {
                    judgeOrs.addAll(ActivityUtil.getProcessNodeCondition(ActivityUtil.getProcessNodeCompleteCondition()));
                }
            }
            this.initSequenceFlow(SequenceFlowTypeEnum.COMPLETE, preNode, flowNode, null, judgeOrs, negation);
        }

        if (flowNode instanceof ServiceTask) {
            // 机器人配置
            RobotConfigDTO robotConfigDTO = node.getRobotConfig();
            ServiceTask serviceTask = (ServiceTask) flowNode;
            serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
            serviceTask.setImplementation(String.format("${%s}", RobotDelegate.class.getSimpleName()));

            List<CustomProperty> customProperties = serviceTask.getCustomProperties();
            CustomProperty robotConfigProperty = new CustomProperty();
            robotConfigProperty.setName(RobotDelegate.ROBOT_CONFIG_EXPRESSION);
            robotConfigProperty.setSimpleValue(JSON.toJSONString(robotConfigDTO));
            customProperties.add(robotConfigProperty);
        }

        FlowNode currentFlowNode = flowNode;
        // 分支节点
        InclusiveGateway inclusiveGateway = this.initBranches(currentFlowNode, node);
        if (null != inclusiveGateway) {
            currentFlowNode = inclusiveGateway;
        }
        // 子节点
        if (null != node.getNextNode()) {
            FlowNode endNode = this.init(currentFlowNode, node.getNextNode(), returnEndNode);
            if (returnEndNode) {
                currentFlowNode = endNode;
            }
        }

        return currentFlowNode;
    }

    /**
     * 添加一个特殊停止节点，用于操作 回退节点时，终止其他并行任务
     */
    private void addStopNode() {
        EndEvent endEvent = new EndEvent();
        String id = ActivityUtil.generateStopNodeId();
        endEvent.setId(id);
        endEvent.setName(id);
        this.handlerGraphicInfo(endEvent, 1, ActivityUtil.FLOW_STEP);
        this.process.addFlowElement(endEvent);
    }

    /**
     * 添加一个审批拒绝节点
     */
    private void addRefuseNode() {
        EndEvent endEvent = new EndEvent();
        String id = ActivityUtil.generateRefuseNodeId();
        endEvent.setId(id);
        endEvent.setName(id);

        TerminateEventDefinition terminateEventDefinition = new TerminateEventDefinition();
        terminateEventDefinition.setTerminateAll(true);
        terminateEventDefinition.setTerminateMultiInstance(true);
        endEvent.setEventDefinitions(Collections.singletonList(terminateEventDefinition));
        this.handlerGraphicInfo(endEvent, 1, ActivityUtil.FLOW_STEP);
        this.process.addFlowElement(endEvent);
    }

    /**
     * 创建会话
     *
     * @param preNode     preNode
     * @param nodeBaseDTO nodeBaseDTO
     * @return FlowElement
     */
    private FlowNode createElement(FlowNode preNode, NodeBaseDTO nodeBaseDTO, GraphicInfo preGraphicInfo, boolean pushToMap) {
        FlowNode flowNode;

        String flowId = ActivityUtil.generateFlowNodeId(nodeBaseDTO);
        List<ActivitiListener> executionListeners = new ArrayList<>();
        List<ActivitiListener> taskListeners = new ArrayList<>();

        boolean containsFlow = this.process.getFlowElement(flowId) instanceof FlowNode;
        boolean isRefuse = false;
        boolean isCarbonCopy = false;
        boolean isApproval = false;
        switch (NodeTypeEnum.isExistById(nodeBaseDTO.getNodeType())) {
            case START_EVENT:
                flowNode = new StartEvent();
                break;
            case END_EVENT:
                if (containsFlow) {
                    flowNode = (FlowNode) this.process.getFlowElement(flowId);
                } else {
                    flowNode = new EndEvent();
                    if (nodeBaseDTO.getNodeId().compareTo(0L) >= 0) {
                        // 流程完成配置执行监听器 nodeBaseDTO.getNodeId() > 0
                        executionListeners.add(BpmnModelUtil.createExecutionListener(EventTypeEnum.START, TaskCompletedListener.class));
                    } else {
                        isRefuse = true;
                    }
                }
                break;
            case INCLUSIVE_GATEWAY_NODE:
            case BRANCH_INCLUSIVE_GATEWAY_NODE:
                // 网关
                flowNode = new InclusiveGateway();
                break;
            case EXCLUSIVE_GATEWAY_NODE:
                // 网关
                flowNode = new ExclusiveGateway();
                break;
            case APPROVAL_NODE:
                // 审批节点
                flowNode = new UserTask();
                isApproval = true;
                break;
            case FILL_IN_NODE:
                // 填写节点
                // 抄送节点
                flowNode = new UserTask();
                break;
            case CARBON_COPY_NODE:
                // 抄送节点
                flowNode = new UserTask();
                isCarbonCopy = true;
                break;
            case BOUNDARY_EVENT_NODE:
                BoundaryEvent boundaryEvent = new BoundaryEvent();
                if (preNode instanceof UserTask) {
                    boundaryEvent.setAttachedToRefId(preNode.getId());
                    boundaryEvent.setAttachedToRef((UserTask) preNode);
                }
                flowNode = boundaryEvent;
                break;
            case ADD_ROBOT_NODE:
                // Robot·自动添加数据
                ServiceTask addRobot = new ServiceTask();
                addRobot.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                addRobot.setImplementation(String.format("${%s}", RobotDelegate.class.getSimpleName()));
                addRobot.setExtensionId("testExtensionId");
                flowNode = addRobot;
                break;
            case MODIFY_ROBOT_NODE:
                // Robot·自动更新数据
                ServiceTask modifyRobot = new ServiceTask();
                modifyRobot.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                modifyRobot.setImplementation(String.format("${%s}", RobotDelegate.class.getSimpleName()));
                modifyRobot.setExtensionId("testExtensionId");
                flowNode = modifyRobot;
                break;
            default:
                throw new BusinessException(BaseEnum.RUN_ERROR);
        }

        if (!containsFlow) {
            flowNode.setId(flowId);
            flowNode.setName(nodeBaseDTO.getNodeName());
        }

        if (pushToMap) {
            // 坐标
            double x = 1, y = 1;
            if (null != preNode) {
                // 坐标
                if (null == preGraphicInfo) {
                    preGraphicInfo = this.bpmnModel.getGraphicInfo(preNode.getId());
                    preGraphicInfo = null == preGraphicInfo ? new GraphicInfo() : preGraphicInfo;
                }
                x = preGraphicInfo.getX();
                y = preGraphicInfo.getY();
                if (preNode instanceof InclusiveGateway) {
                    if (currentParallelNodeHeightMap.containsKey(preNode.getId())) {
                        y = ActivityUtil.flowStep(currentParallelNodeHeightMap.get(preNode.getId()), 2);
                    } else {
                        y = preGraphicInfo.getY();
                    }
                    x += ActivityUtil.FLOW_STEP;
                    currentParallelNodeHeightMap.put(preNode.getId(), y);
                } else if (isRefuse) {
                    y = ActivityUtil.flowStep(preGraphicInfo.getY(), 1);
                } else {
                    x += ActivityUtil.FLOW_STEP;
                }
            }
            this.handlerGraphicInfo(flowNode, x, y);

            if (containsFlow) {
                // 当前节点入箭头 目标坐标更新
                flowNode.getIncomingFlows().forEach(incomingFlow -> {
                    GraphicInfo flowSourceGraphicInfo = BeanUtil.copyProperties(this.bpmnModel.getGraphicInfo(incomingFlow.getSourceRef()), GraphicInfo.class);
                    flowSourceGraphicInfo.setX(flowSourceGraphicInfo.getX() + 100);
                    flowSourceGraphicInfo.setY(flowSourceGraphicInfo.getY() + 50);

                    GraphicInfo flowTargetGraphicInfo = BeanUtil.copyProperties(this.bpmnModel.getGraphicInfo(flowId), GraphicInfo.class);
                    flowTargetGraphicInfo.setY(flowTargetGraphicInfo.getY() + 50);

                    this.bpmnModel.removeLabelGraphicInfo(incomingFlow.getId());
                    this.bpmnModel.addFlowGraphicInfoList(incomingFlow.getId(), Arrays.asList(flowSourceGraphicInfo, flowTargetGraphicInfo));
                });
            }
        }

        if (!containsFlow) {

            // 执行监听器-节点开始
            executionListeners.add(BpmnModelUtil.createExecutionListener(EventTypeEnum.START, FlowStartListener.class));

            if (flowNode instanceof UserTask) {
                UserTask userTask = (UserTask) flowNode;

                // 执行监听器-处理人
                taskListeners.add(BpmnModelUtil.createTaskListener(EventTypeEnum.CREATE, TaskHandlerListener.class));

                // 执行监听器-通知
                executionListeners.add(BpmnModelUtil.createExecutionListener(EventTypeEnum.START, TaskNoticeListener.class));

                // 执行监听器-会签/或签
                if (isApproval) {
                    this.initCountersign(userTask, nodeBaseDTO);
                }

                // 会话监听器-抄送自动流转
                if (isCarbonCopy) {
                    taskListeners.add(BpmnModelUtil.createTaskListener(EventTypeEnum.CREATE, TaskAutoCompleteListener.class));
                }

                // 会话监听器
                userTask.setTaskListeners(taskListeners);

                nodeTaskMap.put(nodeBaseDTO.getNodeId(), (Task) flowNode);
            }

            // 配置执行监听器-流转
            executionListeners.add(BpmnModelUtil.createExecutionListener(EventTypeEnum.END, TaskCompleteListener.class));

            // 执行监听器
            flowNode.setExecutionListeners(executionListeners);

            if (!(flowNode instanceof BoundaryEvent)) {
                this.process.addFlowElement(flowNode);
            }
        }
        return flowNode;
    }

    /**
     * 创建连线
     *
     * @param flowTypeEnum flowTypeEnum
     * @param source       source
     * @param target       target
     * @param flowId       指定生成的id
     * @param judgeOrs     条件
     * @param negation     条件取反
     */
    private void initSequenceFlow(SequenceFlowTypeEnum flowTypeEnum, FlowNode source, FlowNode target, String flowId, List<JudgeOr> judgeOrs, boolean negation) {
        SequenceFlow sequenceFlow = BpmnModelUtil.createSequenceFlow(flowTypeEnum, source, target, flowId, judgeOrs, negation);

        // 连接线添加到流程定义中
        this.process.addFlowElement(sequenceFlow);

        // 坐标
        GraphicInfo flowSourceGraphicInfo = BeanUtil.copyProperties(this.bpmnModel.getGraphicInfo(source.getId()), GraphicInfo.class);
        GraphicInfo flowTargetGraphicInfo = BeanUtil.copyProperties(this.bpmnModel.getGraphicInfo(target.getId()), GraphicInfo.class);

        flowSourceGraphicInfo.setX(flowSourceGraphicInfo.getX() + 100);
        flowSourceGraphicInfo.setY(flowSourceGraphicInfo.getY() + 50);

        flowTargetGraphicInfo.setY(flowTargetGraphicInfo.getY() + 50);
        this.bpmnModel.addFlowGraphicInfoList(sequenceFlow.getId(), Arrays.asList(flowSourceGraphicInfo, flowTargetGraphicInfo));

        // 入箭头
        List<SequenceFlow> incomingFlows = target.getIncomingFlows();
        incomingFlows.add(sequenceFlow);
        target.setIncomingFlows(incomingFlows);

        // 当前节点 出箭头
        List<SequenceFlow> outgoingFlows = source.getOutgoingFlows();
        outgoingFlows.add(sequenceFlow);
        source.setOutgoingFlows(outgoingFlows);
    }

    /**
     * 生成或签/会签配置
     *
     * @param userTask    userTask
     * @param nodeBaseDTO nodeBaseDTO
     */
    private void initCountersign(UserTask userTask, NodeBaseDTO nodeBaseDTO) {
        if (ApprovalTypeEnum.ALL_PASS.getId() == nodeBaseDTO.getApprovalType()) {
            MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
            // Sequential：执行顺序。必选项，可选值有true、false。用于设置多实例的执行顺序。True：多实例顺序执行，false：多实例并行
            multiInstanceLoopCharacteristics.setSequential(false);
/*
            // 高级用法 审批人
            // 元素变量。选择Collection时必选，为collection集合每次遍历的元素
            multiInstanceLoopCharacteristics.setElementVariable(ActivityUtil.ASSIGNEE);
            // 集合。可选项。会签人数的集合，通常为list。和loop cardinality二选一
            multiInstanceLoopCharacteristics.setInputDataItem(ActivityUtil.ASSIGNEE_LIST);
*/

            // 这里需要介绍一下会签环节中设计的几个默认流程变量
            // nrOfInstances（numberOfInstances）：会签中总共的实例数
            // nrOfCompletedInstances：已经完成的实例数量
            // nrOfActiveInstances：当前活动的实例数量，即还没有完成的实例数量
            // 条件${nrOfInstances == nrOfCompletedInstances}表示所有人员审批完成后会签结束。
            // 条件${ nrOfCompletedInstances == 1}表示一个人完成审批，该会签就结束。
            String completionCondition = String.format("${%s==%s}", ActivityUtil.nrOfCompletedInstances, ActivityUtil.nrOfInstances);
            multiInstanceLoopCharacteristics.setCompletionCondition(completionCondition);

            // loop cardinality：循环基数。可选项。可以直接填整数，表示会签的人数。
            multiInstanceLoopCharacteristics.setLoopCardinality(String.format("${%s}", ActivityUtil.nrOfInstances));
            userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        }
    }

    /**
     * 生成流程超时配置
     *
     * @param flowNode    flowNode
     * @param nodeRespDTO nodeRespDTO
     */
    private void initOvertime(FlowNode flowNode, NodeRespInfoDTO nodeRespDTO) {
        if (NodeTypeEnum.APPROVAL_NODE.getId() == nodeRespDTO.getNodeType()
                || NodeTypeEnum.FILL_IN_NODE.getId() == nodeRespDTO.getNodeType()) {
            UserTask userTask = (UserTask) flowNode;
            // 审批节点和填写节点支持超时配置
            if (SwitchEnum.YES.getId() == nodeRespDTO.getNodeOvertime()) {
                /*
                 * 超时配置格式说明：R2/2015-06-04T19:25:16.828696-07:00/P1DT10S
                 * R2：重复次数
                 *  R - 将永远重复
                 *  R1 - 将重复一次
                 *  R231 - 将重复231次。
                 * 2015-06-04T19:25:16.828696-07:00：开始时间
                 *  任务第一次运行的时间。如果开始日期时间已经过去，Kala将返回一个错误。
                 *  其中"T"用来分割日期和时间，时间后面跟着的"-07:00"表示西七区，注意"-"是连字符，不是减号。
                 *  时区默认是0时区，可以用"Z"表示，也可以不写。
                 *  对于我国，要使用"+08:00"，表示东八区。
                 *  上面的字符串表示 2015年6月4日，19点25分16秒828696纳秒，西七区。
                 * P1DT10S：运行间隔
                 *  运行间隔以"P"开始，和上面一样也是用"T"分割日期和时间，如P1Y2M10DT2H30M15S。注意如果没有年月日，"T"也不能省略
                 *  P 开始标记
                 *  1Y - 一年
                 *  2M - 两个月
                 *  10D - 十天
                 *  T - 时间和日期分的割标记
                 *  2H - 两个小时
                 *  30M - 三十分钟
                 *  15S 十五秒钟
                 */

                TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
                if (OvertimeTriggerTypeEnum.FORM_DATA.getId() == nodeRespDTO.getTriggerType()) {
                    // 表单值 表单配置的时间 指定时间前/后 视为超时
                    if (null != nodeRespDTO.getTriggerField()) {
                        String processCompleteKey = ActivityUtil.getProcessCompleteKey(nodeRespDTO.getTriggerField());
                        timerEventDefinition.setTimeDuration(String.format("${'R1/'%s/%s}", processCompleteKey, ""));
                    }
                } else {
                    // 自定义值 流程到达该节点的时间 指定时间后 视为超时
                    if (null != nodeRespDTO.getTriggerCondition() && null != nodeRespDTO.getTriggerConditionUnit()) {
                        String flowNodeStartTimeKey = ActivityUtil.generateFlowNodeStartTimeKey(userTask.getId());
                        timerEventDefinition.setTimeDuration(String.format("${'R1/'%s/%s}", flowNodeStartTimeKey, ""));
                    }
                }

                NodeBaseDTO nodeBaseDTO = new NodeBaseDTO();
                nodeBaseDTO.setNodeId(nodeRespDTO.getNodeId());
                nodeBaseDTO.setNodeName(nodeRespDTO.getNodeName());
                nodeBaseDTO.setNodeType(NodeTypeEnum.BOUNDARY_EVENT_NODE.getId());
                BoundaryEvent boundaryEvent = (BoundaryEvent) this.createElement(userTask, nodeBaseDTO, null, false);
                List<EventDefinition> eventDefinitions = boundaryEvent.getEventDefinitions();
                eventDefinitions.add(timerEventDefinition);
                boundaryEvent.setEventDefinitions(eventDefinitions);
            }
        }
    }

    /**
     * 生成流程并行节点
     *
     * @param flowNode    flowNode
     * @param nodeRespDTO nodeRespDTO
     * @return ExclusiveGateway
     */
    private InclusiveGateway initBranches(FlowNode flowNode, NodeRespInfoDTO nodeRespDTO) {
        if (CollectionUtil.isNotEmpty(nodeRespDTO.getBranches())) {
            /* 创建一个网关节点
             *                         -> 网关分支 ->
             * 当前节点 -> 网关节点                 排他网关节点
             *                         -> 网关分支 ->
             * */
            InclusiveGateway preInclusiveGateway = (InclusiveGateway) this.getGatewayByFlowNode(flowNode, nodeRespDTO, NodeTypeEnum.INCLUSIVE_GATEWAY_NODE);

            /* 创建一个排他网关节点
             * 网关分支 ->
             *             排他网关节点 (备注：这个方法结束后， 排他网关节点 -> 当前节点的下一节点)
             * 网关分支 ->
             * */
            NodeTypeEnum nodeTypeEnum = NodeTypeEnum.BRANCH_INCLUSIVE_GATEWAY_NODE;
            NodeBaseDTO branchInclusiveGatewayNode = new NodeBaseDTO();
            branchInclusiveGatewayNode.setNodeId(-nodeRespDTO.getNodeId());
            branchInclusiveGatewayNode.setNodeType(nodeTypeEnum.getId());
            branchInclusiveGatewayNode.setNodeName(String.format("%s%s", nodeRespDTO.getNodeName(), nodeTypeEnum.getName()));
            InclusiveGateway endInclusiveGateway = null;

            List<JudgeOr> defaultEndFlowOrs = new ArrayList<>();
            for (NodeRespInfoDTO branchNode : nodeRespDTO.getBranches()) {
                NodeRespInfoDTO nextNode = branchNode.getNextNode();
                GatewayConfigDTO gatewayConfig = branchNode.getGatewayConfig();
                defaultEndFlowOrs.addAll(gatewayConfig.getJudgeOr());

                FlowNode source;
                // 分支中的分支
                InclusiveGateway branchBranchNode = this.initBranches(preInclusiveGateway, branchNode);
                if (null != branchBranchNode) {
                    source = branchBranchNode;
                } else if (null != nextNode) {
                    nextNode.setGatewayConfig(gatewayConfig);
                    source = this.init(preInclusiveGateway, nextNode, true);
                } else {
                    source = preInclusiveGateway;
                }
                if (null == endInclusiveGateway) {
                    GraphicInfo preGraphicInfo = this.bpmnModel.getGraphicInfo(source.getId());
                    endInclusiveGateway = (InclusiveGateway) this.createElement(flowNode, branchInclusiveGatewayNode, preGraphicInfo, true);
                }

                SequenceFlowTypeEnum complete = SequenceFlowTypeEnum.COMPLETE;

                String flowId = null;
                if (null == nextNode) {
                    flowId = ActivityUtil.connector(complete, ActivityUtil.generateFlowNodeId(branchNode), endInclusiveGateway.getId());
                }
                List<JudgeOr> ors = ActivityUtil.getProcessNodeCondition(ActivityUtil.getProcessNodeCompleteCondition());
                this.initSequenceFlow(complete, source, endInclusiveGateway, flowId, ors, false);
            }

            // 添加分支默认流转分支
            NodeRespInfoDTO defaultEndNode = new NodeRespInfoDTO();
            defaultEndNode.setNodeId(nodeRespDTO.getEndNode().getNodeId());
            defaultEndNode.setNodeType(NodeTypeEnum.END_EVENT.getId());
            defaultEndNode.setNodeName(HandleTypeEnum.APPROVED.getName());
            GatewayConfigDTO gatewayConfigDTO = new GatewayConfigDTO();
            gatewayConfigDTO.setJudgeOr(defaultEndFlowOrs);
            // 条件反转
            gatewayConfigDTO.setNegation(true);
            defaultEndNode.setGatewayConfig(gatewayConfigDTO);
            this.init(preInclusiveGateway, defaultEndNode, false);

            return endInclusiveGateway;
        }
        return null;
    }

    /**
     * 获取当前节点 的流转网关节点
     *
     * @param flowNode    flowNode
     * @param nodeRespDTO nodeRespDTO
     * @return ExclusiveGateway
     */
    private Gateway getGatewayByFlowNode(FlowNode flowNode, NodeBaseDTO nodeRespDTO, NodeTypeEnum nodeTypeEnum) {
        // 当前节点取负数，作为排他网关节点
        NodeBaseDTO gatewayNode = new NodeBaseDTO();
        gatewayNode.setNodeId(-nodeRespDTO.getNodeId());
        gatewayNode.setNodeType(nodeTypeEnum.getId());
        gatewayNode.setNodeName(String.format("%s%s", nodeRespDTO.getNodeName(), nodeTypeEnum.getName()));
        String flowId = ActivityUtil.generateFlowNodeId(gatewayNode);

        // 检查前面是否已经生成当前的节点的网关节点
        if (this.process.getFlowElement(flowId) instanceof Gateway) {
            return (Gateway) this.process.getFlowElement(flowId);
        } else {
            return (Gateway) this.appendFlowNode(flowNode, gatewayNode, SequenceFlowTypeEnum.COMPLETE, new ArrayList<>());
        }
    }

    /**
     * 后方插入一个节点
     *
     * @param flowNode     flowNode
     * @param nodeBaseDTO  nodeBaseDTO
     * @param flowTypeEnum flowTypeEnum
     * @return FlowNode
     */
    private FlowNode appendFlowNode(FlowNode flowNode, NodeBaseDTO nodeBaseDTO, SequenceFlowTypeEnum flowTypeEnum, List<JudgeOr> ors) {
        FlowNode insertFlowNode = this.createElement(flowNode, nodeBaseDTO, null, true);
        this.initSequenceFlow(flowTypeEnum, flowNode, insertFlowNode, null, ors, false);

        return insertFlowNode;
    }

    /**
     * 处理节点坐标
     *
     * @param flowElement flowElement
     * @param x           x
     * @param y           y
     */
    private void handlerGraphicInfo(FlowElement flowElement, double x, double y) {
        GraphicInfo graphicInfo = new GraphicInfo();
        graphicInfo.setWidth(ActivityUtil.FLOW_SIZE);
        graphicInfo.setHeight(ActivityUtil.FLOW_SIZE);
        graphicInfo.setElement(flowElement);
        graphicInfo.setX(x);
        graphicInfo.setY(y);
        this.bpmnModel.addGraphicInfo(flowElement.getId(), graphicInfo);
    }
}
